Skip to content

Support Pydantic structured outputs in OpenAIResponseOperator - #69812

Open
YAshhh29 wants to merge 9 commits into
apache:mainfrom
YAshhh29:feature/openai-structured-outputs
Open

YAshhh29 wants to merge 9 commits into
apache:mainfrom
YAshhh29:feature/openai-structured-outputs

Conversation

@YAshhh29

@YAshhh29 YAshhh29 commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

This adds structured outputs to OpenAIResponseOperator. If you pass a Pydantic model as text_format, the operator calls OpenAIHook.parse_response (a thin wrapper around the SDK's responses.parse) and returns the parsed result as a plain dict via model_dump(mode="json"), so it's safe to push to XCom. Without text_format, nothing changes.

class Person(BaseModel):
    name: str
    age: int

OpenAIResponseOperator(
    task_id="extract_person",
    conn_id="openai_default",
    input_text="Extract the name and age from: 'Alice is 30 years old'.",
    text_format=Person,
)

When something goes wrong, the task fails instead of passing partial data downstream:

  • the response didn't complete (for example it hit max_output_tokens), even if the partial JSON happens to validate
  • there's no parsed output, like a refusal or a response that only made tool calls
  • the SDK can't parse the output at all (usually truncation), in which case its ValidationError becomes a ValueError that names the model

The error includes the response id and whatever the API sent back: status, error, incomplete details, the refusal text or the output item types.

It fits the rest of the operator. max_output_tokens, max_tool_calls and response_kwargs work the same for structured requests, and response_id and usage are pushed to XCom for both paths. For structured responses they're pushed before the output is checked, so even a rejected response records what it cost. text_format is keyword-only and checked when the operator is created, because the SDK also accepts pydantic dataclasses, which would only fail after the paid API call.

Testing

  • Unit tests pass on openai 2.37.0, 2.46.0 and 2.54.0
  • End-to-end through a real OpenAIHook and SDK client with only the HTTP layer faked: success, refusal, incomplete-but-valid, truncated JSON, a failed response and the plain-text path
  • I removed each safeguard one at a time to make sure a test catches it
  • An earlier version ran live against the OpenAI API on a from-source main build (screenshots in the comments below)

Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Claude Opus 5.5); earlier iterations with GitHub Copilot

Generated-by: Claude Code (Claude Opus 5.5) following the guidelines

Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py Outdated
Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py Outdated
Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py Outdated
Comment thread providers/openai/src/airflow/providers/openai/hooks/openai.py Outdated
Comment thread providers/openai/tests/unit/openai/hooks/test_openai.py Outdated
YAshhh29 added a commit to YAshhh29/airflow that referenced this pull request Jul 14, 2026
…fusal

Five fixes from the review of apache#69812:

(1) OpenAIResponseOperator now calls model_dump(mode='json'), so non-JSON field types (plain enum.Enum, datetime, UUID, ...) come back as their JSON representations instead of live Python objects. The old default (mode='python') would break XCom push for models with a plain enum field, since Airflow's serde only unwraps enums that mix in str/int -- and the API call has already been paid for by then. mode='json' is what airflow.serialization.serde.serializers.pydantic already uses, so this alignment holds the operator's XCom-safe claim for any model.

(2) parse_response is now wrapped in try/except ValidationError, re-raising as ValueError. responses.parse() raises pydantic.ValidationError internally when the model's JSON output can't be coerced into text_format -- most commonly when the response is truncated mid-JSON on max_output_tokens. Without the wrap, users got a raw pydantic traceback; now they get a single ValueError shape across all parse failures.

(3) The output_parsed=None ValueError now includes the API-reported error and incomplete_details in the message, so users don't need a follow-up OpenAIHook.get_response round-trip to diagnose a refusal.

(4) Dropped the docstring claim that structured outputs require 'gpt-4o-2024-08-06 or later' -- the default gpt-4o-mini already supports them (has since its initial release), and specific model claims rot as new models ship. Also removed the contradictory 'use hook directly for previous_response_id chaining' line, since response_kwargs already forwards previous_response_id to the SDK.

(5) OpenAIHook.parse_response is now generic (TypeVar bound to BaseModel), returning ParsedResponse[T] instead of ParsedResponse[Any]. Callers get output_parsed typed as T | None, matching the SDK's own signature.

Also fixes the failing docs --spellcheck-only CI job by rephrasing 'parseable' -> 'return a structured output' throughout, moves the pydantic import in test_openai.py to module top, adds parse_response to the hook-methods list in openai.rst, drops the redundant model= override from the example DAG, and adds a regression test for both the enum-field dump-mode and the ValidationError-to-ValueError conversion.
@YAshhh29

Copy link
Copy Markdown
Contributor Author

Thanks @kaxil — this is a genuinely great review. Every one of the five catches
would have been a real footgun for users:

  • The model_dump() enum bug would only surface at XCom push, after the API call
    had already been paid for — the exact kind of silent-until-production bug that
    makes structured-output code frustrating to trust.
  • The ValidationError gap was worse: I'd been reasoning about output_parsed=None
    as "the" failure mode, but truncation raises inside parse() before assignment,
    so the operator's whole failure-handling story never fired. That's the sort of
    claim that needs to hold in the docs and didn't, before this review.
  • The previous_response_id / model-requirement contradictions, the
    ParsedResponse[Any] throwing away the SDK's own generic, the pydantic import
    down inside a test function — each fair on its own, and together they add up
    to code that reads like "someone shipped the first working version." Which is
    exactly what happened. Appreciate the read.

All five landed in beb3d9e. Recap:

  1. model_dump(mode="json") — matches what
    airflow.serialization.serde.serializers.pydantic already does. New regression
    test test_openai_response_operator_structured_output_dumps_enum_as_json uses
    a plain str-mixin-free enum.Enum field and asserts the dumped value is
    "high" (str), not a Priority instance. Reverting mode="json" fails it.

  2. ValidationError -> ValueError — parse_response is now wrapped in
    try/except ValidationError, re-raising as ValueError mentioning the
    text_format class name. New test
    test_openai_response_operator_structured_output_validation_error_raises
    feeds a real ValidationError (built via _StructuredPerson.model_validate({}))
    through parse_response.side_effect and asserts the conversion.

  3. Richer refusal message — the output_parsed=None ValueError now
    includes status, error, and incomplete_details from the API response.
    The existing refusal test asserts all three snippets are present in the
    message, so a regression back to "just say status" would fail.

  4. Docstring / example DAG / rst — dropped the "requires
    gpt-4o-2024-08-06 or later" claim from the operator docstring, the hook
    docstring, and the how-to guide. Dropped the model="gpt-4o-2024-08-06"
    override + explanatory comment from the example DAG. Removed
    previous_response_id from the "use hook directly" list in the docstring.

  5. Generic typing + hook-methods list — parse_response is now
    _TextFormatT = TypeVar("_TextFormatT", bound="BaseModel") returning
    ParsedResponse[_TextFormatT]. Callers get output_parsed typed as
    _TextFormatT | None, matching the SDK's own signature. Also added
    parse_response to the Responses bullet in docs/operators/openai.rst.

  6. Test import — from pydantic import BaseModel moved to the module top
    of tests/unit/openai/hooks/test_openai.py.

Bonus: the failing Build documentation (--spellcheck-only) job was tripping
on "parseable" — rephrased across the docstring, the ValueError message, and
the how-to guide to standard English so spellcheck passes.

Author review

I read every fix line-by-line before pushing and validated the two behavioral
claims that mattered most against the installed openai and pydantic packages:
that Responses.parse raises ValidationError inside its own body (so a wrap
at the operator's call site is what catches it), and that model_dump() defaults
to mode="python" while mode="json" renders enum.Enum fields as their JSON
values. Both matched what your review described, so the fixes hold on the same
grounds. The design calls the fixes preserve — one operator (not a new class),
ValueError (not a new exception class), model_dump(mode="json") (not a
custom serializer) — are still mine.


Drafted-by: GitHub Copilot (Claude Opus 4.6); reviewed by @YAshhh29 before posting

YAshhh29 added a commit to YAshhh29/airflow that referenced this pull request Jul 14, 2026
…fusal

Five fixes from the review of apache#69812:

(1) OpenAIResponseOperator now calls model_dump(mode='json'), so non-JSON field types (plain enum.Enum, datetime, UUID, ...) come back as their JSON representations instead of live Python objects. The old default (mode='python') would break XCom push for models with a plain enum field, since Airflow's serde only unwraps enums that mix in str/int -- and the API call has already been paid for by then. mode='json' is what airflow.serialization.serde.serializers.pydantic already uses, so this alignment holds the operator's XCom-safe claim for any model.

(2) parse_response is now wrapped in try/except ValidationError, re-raising as ValueError. responses.parse() raises pydantic.ValidationError internally when the model's JSON output can't be coerced into text_format -- most commonly when the response is truncated mid-JSON on max_output_tokens. Without the wrap, users got a raw pydantic traceback; now they get a single ValueError shape across all parse failures.

(3) The output_parsed=None ValueError now includes the API-reported error and incomplete_details in the message, so users don't need a follow-up OpenAIHook.get_response round-trip to diagnose a refusal.

(4) Dropped the docstring claim that structured outputs require 'gpt-4o-2024-08-06 or later' -- the default gpt-4o-mini already supports them (has since its initial release), and specific model claims rot as new models ship. Also removed the contradictory 'use hook directly for previous_response_id chaining' line, since response_kwargs already forwards previous_response_id to the SDK.

(5) OpenAIHook.parse_response is now generic (TypeVar bound to BaseModel), returning ParsedResponse[T] instead of ParsedResponse[Any]. Callers get output_parsed typed as T | None, matching the SDK's own signature.

Also fixes the failing docs --spellcheck-only CI job by rephrasing 'parseable' -> 'return a structured output' throughout, moves the pydantic import in test_openai.py to module top, adds parse_response to the hook-methods list in openai.rst, drops the redundant model= override from the example DAG, and adds a regression test for both the enum-field dump-mode and the ValidationError-to-ValueError conversion.
@YAshhh29
YAshhh29 force-pushed the feature/openai-structured-outputs branch from beb3d9e to 59297e5 Compare July 14, 2026 04:46
@kaxil

kaxil commented Jul 14, 2026

Copy link
Copy Markdown
Member

Could you also run a sample Dag with the this, and show screenshot of Airflow UI to show status and logs please

@YAshhh29

Copy link
Copy Markdown
Contributor Author

Will do. This branch has been developed and unit-tested on a Windows workstation,
so producing a real-run screenshot means standing up an Airflow environment
(Codespaces or Docker Compose) — a few days of setup on my end. I've also got
end-term exams through the 19th, so realistically will update with screenshots around
July 20. Grid view / logs / XCom in one reply once the env is running. Will
bump this thread if I slip.

@YAshhh29

YAshhh29 commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Done — ran the text_format path end-to-end against the real OpenAI Responses API. Thanks @kaxil for the nudge to verify live.

Environment: Airflow standalone on this branch (feature/openai-structured-outputs) with the branch openai provider, LocalExecutor, a real OpenAI account (api.openai.com), and the default gpt-4o-mini model.

Sample DAG (openai_structured_smoke) — one OpenAIResponseOperator task with a Pydantic text_format:

class Person(BaseModel):
    name: str
    age: int

OpenAIResponseOperator(
    task_id="extract_person",
    conn_id="openai_default",
    model="gpt-4o-mini",
    input_text="Extract the name and age from: 'Alice is 30 years old.'",
    text_format=Person,
)

Result: the task succeeds and pushes the parsed structured output to XCom as a plain dict — {"name": "Alice", "age": 30} — no manual serialization, exactly the flow this PR adds. gpt-4o-mini (the default) handled structured outputs fine, matching your note about dropping the specific-model claim.

Screenshots attached below:

  1. Grid — status: extract_person finished success with Operator OpenAIResponseOperator.
  2. Task logs: the openai_default connection is retrieved and the operator emits Generated response resp_… — a real Responses API call went out and returned.
  3. XCom: the task's return_value is the parsed model as a plain dict — {"name": "Alice", "age": 30}.
  4. Terminal (airflow dags test): explicit proof of the live call — POST https://api.openai.com/v1/responses "HTTP/1.1 200 OK", followed by SetXCom(... value={'name': 'Alice', 'age': 30} ...) and state=success.
Screenshot 2026-07-22 004032 Screenshot 2026-07-22 004103 Screenshot 2026-07-22 004139 Screenshot 2026-07-22 004306

@kaxil

kaxil commented Jul 21, 2026

Copy link
Copy Markdown
Member

Formatting is off -- can't see any images :)

image

@YAshhh29

Copy link
Copy Markdown
Contributor Author

Fixed — dragged the images in this time instead of pasting raw <img> tags (rookie move on my part 😅).
All four screenshots should render now: Grid status, task logs, XCom, and the terminal 200 OK from api.openai.com/v1/responses.

@kaxil

kaxil commented Jul 21, 2026

Copy link
Copy Markdown
Member

@YAshhh29 The screenshot doesn't look from the main/ your branch, what version of Airflow is your environment?

@YAshhh29

Copy link
Copy Markdown
Contributor Author

You're right that the UI isn't from main. The environment is Airflow 3.0.3 (a released core wheel), with my PR branch's openai provider installed on top of it. My PR only touches the openai provider (which is versioned independently and targets Airflow 3.x), so the core being 3.0.3 doesn't affect the text_format path — but I understand why the older UI raised a flag.

Here's proof the running provider is this branch's code, not the released 1.8.0 (which has no text_format):

Screenshot 2026-07-22 022218

So the feature itself genuinely ran on the branch code — the mismatch is only the core UI version, not the operator.

That said, I'll redo the run against a from-source main build (core installed editable from the branch, not the 3.0.3 wheel) so the UI matches main and there's no ambiguity. I'll re-capture the Grid/logs/XCom on main's UI and update this thread shortly.


@YAshhh29

Copy link
Copy Markdown
Contributor Author

The earlier run was on 3.0.3 core (was pre-installed in my Codespace) with the branch openai provider on top, which is why the UI looked older. That combo still exercised the change, but you're right that it wasn't the right proof.

I re-ran the smoke DAG on Airflow 3.4.0 from source (main) with the openai provider from this branch, in a clean AIRFLOW_HOME. Same DAG — extract Person(name: str, age: int) from "Alice is 30 years old." — hits the real OpenAI Responses API. Screenshots include:

  1. Grid — extract_person finished green in ~6s, one try, OpenAIResponseOperator
  2. Logs — Generated response resp_0eb37e37… from airflow.providers.openai.operators.openai.OpenAIResponseOperator, followed by the XCom push
  3. XCom — return_value key present at the run's end timestamp
  4. Terminal — same Generated response resp_… line pulled from the log file

Happy to add a second smoke case (validation-failure branch, or the dict-fallback in parse_response) if you want that covered too — just say the word.

Screenshot 2026-07-22 033030 Screenshot 2026-07-22 033445 Screenshot 2026-07-22 032803 image image

YAshhh29 added a commit to YAshhh29/airflow that referenced this pull request Jul 23, 2026
…fusal

Five fixes from the review of apache#69812:

(1) OpenAIResponseOperator now calls model_dump(mode='json'), so non-JSON field types (plain enum.Enum, datetime, UUID, ...) come back as their JSON representations instead of live Python objects. The old default (mode='python') would break XCom push for models with a plain enum field, since Airflow's serde only unwraps enums that mix in str/int -- and the API call has already been paid for by then. mode='json' is what airflow.serialization.serde.serializers.pydantic already uses, so this alignment holds the operator's XCom-safe claim for any model.

(2) parse_response is now wrapped in try/except ValidationError, re-raising as ValueError. responses.parse() raises pydantic.ValidationError internally when the model's JSON output can't be coerced into text_format -- most commonly when the response is truncated mid-JSON on max_output_tokens. Without the wrap, users got a raw pydantic traceback; now they get a single ValueError shape across all parse failures.

(3) The output_parsed=None ValueError now includes the API-reported error and incomplete_details in the message, so users don't need a follow-up OpenAIHook.get_response round-trip to diagnose a refusal.

(4) Dropped the docstring claim that structured outputs require 'gpt-4o-2024-08-06 or later' -- the default gpt-4o-mini already supports them (has since its initial release), and specific model claims rot as new models ship. Also removed the contradictory 'use hook directly for previous_response_id chaining' line, since response_kwargs already forwards previous_response_id to the SDK.

(5) OpenAIHook.parse_response is now generic (TypeVar bound to BaseModel), returning ParsedResponse[T] instead of ParsedResponse[Any]. Callers get output_parsed typed as T | None, matching the SDK's own signature.

Also fixes the failing docs --spellcheck-only CI job by rephrasing 'parseable' -> 'return a structured output' throughout, moves the pydantic import in test_openai.py to module top, adds parse_response to the hook-methods list in openai.rst, drops the redundant model= override from the example DAG, and adds a regression test for both the enum-field dump-mode and the ValidationError-to-ValueError conversion.
@YAshhh29
YAshhh29 force-pushed the feature/openai-structured-outputs branch from e33ac87 to a56f704 Compare July 23, 2026 15:25
@YAshhh29
YAshhh29 requested a review from kaxil July 30, 2026 09:52
Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py Outdated
Comment thread providers/openai/tests/unit/openai/operators/test_openai.py Outdated
) from exc

self.log.info("Generated response %s", parsed.id)
if parsed.output_parsed is None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One gap left on this path: the SDK parses output regardless of response status, so a status='incomplete' response whose emitted JSON still validates (a truncated list field with no min-length constraint, or content_filter cutting after a complete object) sails through here and pushes partial data to XCom as task success. The window is narrow -- truncation usually breaks the JSON and lands in the ValidationError branch -- but the plain-text path below does check response.status != "completed" while this one doesn't. A one-line status guard before this check, reusing the same details harvesting, closes it; worth a regression test that status='incomplete' with a valid parsed model raises.

# can't be coerced into ``text_format`` — most commonly because the response
# was truncated (e.g. ``max_output_tokens`` hit) mid-JSON. Convert to a clean
# ``ValueError`` so callers see a consistent shape across all parse failures.
raise ValueError(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch raises before a ParsedResponse exists, so the message carries no response id and no incomplete_details -- users can't even run hook.get_response(id) to confirm reason='max_output_tokens', and the guide now promises "ValueError with the API-reported status, error and incomplete_details" for this case too. Cheapest fix: name truncation (max_output_tokens) as the likely cause in this message, and split the rst sentence to describe the two branches separately.

Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py
Comment thread providers/openai/tests/unit/openai/operators/test_openai.py Outdated
@YAshhh29
YAshhh29 requested a review from kaxil July 30, 2026 16:29
@YAshhh29

Copy link
Copy Markdown
Contributor Author

@kaxil, gentle follow-up on this one. I addressed the review findings, reran the sample Dag against a from-source main build and the real OpenAI Responses API, and posted the Grid, logs, and XCom evidence above. Current checks are green and the PR is mergeable.

Could you take another look when convenient, or let me know what still needs work?

Wraps the SDK's responses.parse(), which turns a Pydantic model into a
strict JSON-schema request and returns a ParsedResponse whose
output_parsed is an instance of that model, or None when the response
carries no parsed output. The method is generic over the model type,
mirroring the SDK's own TextFormatT, so callers get output_parsed typed
as that model rather than Any.
The response_id and usage pushes move verbatim into
_push_response_metadata so the structured-output path added next can
record the same metadata. No behaviour change.
Pass a Pydantic BaseModel subclass as text_format and the operator calls
OpenAIHook.parse_response instead of create_response, returning the
parsed model's model_dump(mode="json") so enums, dates and other
non-JSON field types are safe to push to XCom.

The structured path fails the task rather than returning partial data. A
response whose status is not "completed" raises even when its partial
JSON validates, as does one with no parsed output (a refusal, or a
tools-only response). The SDK's ValidationError for output it cannot
parse, usually truncation at max_output_tokens, is re-raised as
ValueError naming the model. Errors carry the response id and whatever
the API reported: status, error, incomplete_details, refusal text or
output item types.

text_format is keyword-only and validated when the operator is
constructed, since a pydantic dataclass, which the SDK also accepts,
would only fail after the billed call returned. Token ceilings and
templated response_kwargs go through _build_response_kwargs() as on the
text path, and response_id and usage are pushed before the structured
output is checked, so a rejected response still records what it cost.
Responses are real ParsedResponse objects built with model_construct and
real output items rather than Mocks, so an SDK field rename breaks the
tests instead of production. Covers the JSON-mode dump with a plain Enum
(the test fails if mode="json" is reverted), token ceilings reaching
parse_response as ints, invalid ceilings failing before any request,
XCom metadata including for rejected responses, refusal, tools-only,
incomplete-but-valid and failed responses, the ValidationError
conversion, and text_format validation.
Adds a structured outputs section with an example Dag task, lists
parse_response among the hook's Responses methods, and spells out where
the structured path differs from the plain-text one: it fails on an
incomplete response instead of returning truncated output, and records
response_id and usage before checking the output.
@YAshhh29
YAshhh29 force-pushed the feature/openai-structured-outputs branch from 16ff973 to 92cf356 Compare September 25, 2026 20:09
@YAshhh29

Copy link
Copy Markdown
Contributor Author

Hi @kaxil, sorry this one went quiet for so long. I pushed fixes for your July 30 review the same day but never replied to the threads, which I should have. I've answered them now. I'm also trying to move and get more used to Linux, so I can run Breeze and the full checks locally before pushing.

Main changed this operator quite a bit in the meantime (#72049, #72150, #72151), so instead of just fixing the conflicts I wired structured outputs into those changes:

  • max_output_tokens and max_tool_calls now work for structured requests too
  • response_id and usage are pushed to XCom for structured responses as well, before the output is checked, so even a rejected response shows what it cost (same order as OpenAIAgentSessionOperator)
  • text_format is keyword-only now
  • the XCom push moved into a small shared helper, in its own commit with no behaviour change

I tested it on openai 2.37, 2.46 and 2.54, and checked that every safeguard has a test that fails if it's removed. Would you mind taking another look when you get a chance?

cc @Lee-W, since this builds on your recent changes to the operator.

Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py Outdated
Comment thread providers/openai/src/airflow/providers/openai/hooks/openai.py
Comment thread providers/openai/src/airflow/providers/openai/operators/openai.py Outdated
response: Any turned off type checking for every attribute the helper
reads. With ParsedResponse[BaseModel], mypy narrows on output.type and
content.type and checks each field against the SDK, so a renamed or
misspelled field is a type error instead of passing silently.
text_format sat where create_response has model, so
parse_response(prompt, "gpt-4o"), written by analogy with
create_response, passed the model id as the format. model is now the
second positional argument, as in create_response, and text_format is
keyword-only, as in the SDK's responses.parse.
With text_format set, a background response comes back queued or
in_progress, so the status check raises ValueError while the response
keeps running on OpenAI's side. Say so in the response_kwargs docs and
in the guide's background note.
@YAshhh29

Copy link
Copy Markdown
Contributor Author

Thanks for the approval and the extra catches, @kaxil. All three are in, one commit each:

  • _get_structured_response_details is typed as ParsedResponse[BaseModel] now, and mypy catches a misspelled SDK field (checked on 2.37.0)
  • parse_response keeps create_response's positional order, with text_format keyword-only like the SDK
  • the docstring and the guide both say background=True fails a structured request and leaves the response running on OpenAI's side

@YAshhh29
YAshhh29 requested a review from kaxil September 26, 2026 14:02

@kaxil kaxil left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the earlier comments. The typing, the parse_response argument order and the background=True note all look good.

One non-blocking edge now that the operator returns a dict: with multiple_outputs=True, a text_format field named usage or response_id gets pushed as its own XCom after execute, overwriting the metadata this operator pushes under those keys. A line in the text_format docs naming them as reserved would cover it.

@kaxil
kaxil requested review from Lee-W and amoghrajesh September 30, 2026 22:23
With multiple_outputs=True, each top-level field of the structured
result is pushed as its own XCom after execute returns, so a field named
response_id or usage would overwrite the metadata the operator pushes
under those keys. The text_format docstring and the guide now say so.
@YAshhh29

Copy link
Copy Markdown
Contributor Author

Thanks @kaxil! Good catch on multiple_outputs=True. I added a line to the text_format docstring and the guide saying response_id and usage are reserved field names, since a field with either name would overwrite the XCom the operator pushes under that key. Pushed in 40bc1fe.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants